﻿/*****************************************************************
 * @file    key_task.c
 * @brief key handler, achieve key anti-shale and key logic function
 *****************************************************************
 */

#include "key_task.h"
#include "key.h"
#include "timers.h"

/* key state */
typedef enum
{
    KEY_STATE_UP,
    KEY_STATE_DOWN,
    KEY_STATE_NONE
} KeyState;

/* key struct, include some atribute */
typedef struct
{
    uint32_t            pin;        /*!< key pin */
    const EventBits_t   shortEvent; /*!< key publish short event */
    const EventBits_t   longEvent;  /*!< key publish long event */
    TimerHandle_t       timer;      /*!< key timer */
    KeyState            state;      /*!< key state */
    KeyAction           action;     /*!< key action */
}KeyStruct_t;

/* four keys object */
static KeyStruct_t key4 = {
    .pin = SW4,
    .shortEvent = KEY_EVENT_GROUP_SHORT_PRESS_4,
    .longEvent = KEY_EVENT_GROUP_LONG_PRESS_4 };
static KeyStruct_t key5 = {
    .pin = SW5,
    .shortEvent = KEY_EVENT_GROUP_SHORT_PRESS_5,
    .longEvent = KEY_EVENT_GROUP_LONG_PRESS_5 };
static KeyStruct_t key6 = {
    .pin = SW6,
    .shortEvent = KEY_EVENT_GROUP_SHORT_PRESS_6,
    .longEvent = KEY_EVENT_GROUP_LONG_PRESS_6 };
static KeyStruct_t key7 = {
    .pin = SW7,
    .shortEvent = KEY_EVENT_GROUP_SHORT_PRESS_7,
    .longEvent = KEY_EVENT_GROUP_LONG_PRESS_7 };

TaskHandle_t keyTaskHandle;


static void Key_StateCheck(KeyStruct_t *key);
static void Key_ActionCheck(KeyStruct_t *key);
void Key_IsLongPress(TimerHandle_t xTimer);

/**
 * @brief key task
 * @param pvParameters task parameter
 */
void KeyTask(void *pvParameters)
{
    log("[info] key task start\n");

    keyTaskHandle = xTaskGetCurrentTaskHandle();

    uint8_t id;
    key4.timer = xTimerCreate("keyTimer4", 500, pdFALSE, &id, Key_IsLongPress);
    key5.timer = xTimerCreate("keyTimer5", 500, pdFALSE, &id, Key_IsLongPress);
    key6.timer = xTimerCreate("keyTimer6", 500, pdFALSE, &id, Key_IsLongPress);
    key7.timer = xTimerCreate("keyTimer7", 500, pdFALSE, &id, Key_IsLongPress);
    while(1)
    {
        uint32_t keyEvent = 0;

        xTaskNotifyWait(0, UINT32_MAX, &keyEvent, portMAX_DELAY);

        /* different key are distinguished by envent flags and processed accordingly */
        switch((KeyEvent)keyEvent)
        {
        case KEY_EVENT_4:
            Key_StateCheck(&key4);
            Key_ActionCheck(&key4);
            /* clear this event bit, avoid EXTI mistouch  */
            xTaskNotifyWait(0, KEY_EVENT_4, &keyEvent, 0);
            break;
        case KEY_EVENT_5:
            Key_StateCheck(&key5);
            Key_ActionCheck(&key5);
            /* clear this event bit, avoid EXTI mistouch  */
            xTaskNotifyWait(0, KEY_EVENT_5, &keyEvent, 0);
            break;
        case KEY_EVENT_6:
            Key_StateCheck(&key6);
            Key_ActionCheck(&key6);
            /* clear this event bit, avoid EXTI mistouch  */
            xTaskNotifyWait(0, KEY_EVENT_6, &keyEvent, 0);
            break;
        case KEY_EVENT_7:
            Key_StateCheck(&key7);
            Key_ActionCheck(&key7);
            /* clear this event bit, avoid EXTI mistouch  */
            xTaskNotifyWait(0, KEY_EVENT_7, &keyEvent, 0);
            break;
        default:
            break;
        }
    }
}

/**
 * @brief check key down or up
 * @param key key struct pointer
 */
static void Key_StateCheck(KeyStruct_t *key)
{
    /* anti-shake */
    delay_ms(10);
    /* cheke down or up */
    if(gpio_input_bit_get(SW_PORT, key->pin) == RESET)
    {
        key->state = KEY_STATE_DOWN;
        log("key down\n");
    }
    else
    {
        key->state = KEY_STATE_UP;
        log("key up\n");
    }
}

/**
 * @brief check key action
 * @param key
 */
static void Key_ActionCheck(KeyStruct_t *key)
{
    /*
        if key is down, start timer.
        if key is up and no long press, stop timer and judge to be short press.
        if key is up and long press, judge to be none indicate long press end.
     */
    switch(key->state)
    {
    case KEY_STATE_DOWN:
        xTimerStart(key->timer, 0);
        break;
    case KEY_STATE_UP:
        xTimerStop(key->timer, 0);
        if(key->action != KEY_LONG_PRESS)
        {
            key->action = KEY_SHORT_PRESS;
            xEventGroupSetBits(keyEventGroupHandle, key->shortEvent);
            key->action = KEY_NONE;
            // log("short press\n");
        }
        else
        {
            key->action = KEY_NONE;
            // log("long press end\n");
        }
        break;
    }
}

/**
 * @brief judge long press
 * @param xTimer
 */
void Key_IsLongPress(TimerHandle_t xTimer)
{
    /*
        difference key use different timer
        the key is still pressing state after timer overflow, is long press
     */
    if(xTimer == key4.timer)
    {
        if(key4.state == KEY_STATE_DOWN)
        {
            key4.action = KEY_LONG_PRESS;
            xEventGroupSetBits(keyEventGroupHandle, key4.longEvent);
        }
    }
    else if(xTimer == key5.timer)
    {
        if(key5.state == KEY_STATE_DOWN)
        {
            key5.action = KEY_LONG_PRESS;
            xEventGroupSetBits(keyEventGroupHandle, key5.longEvent);
        }
    }
    else if(xTimer == key6.timer)
    {
        if(key6.state == KEY_STATE_DOWN)
        {
            key6.action = KEY_LONG_PRESS;
            xEventGroupSetBits(keyEventGroupHandle, key6.longEvent);
        }
    }
    else if(xTimer == key7.timer)
    {
        if(key7.state == KEY_STATE_DOWN)
        {
            key7.action = KEY_LONG_PRESS;
            xEventGroupSetBits(keyEventGroupHandle, key7.longEvent);
        }
    }
}


